using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommonUtils;
using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;

namespace ShellAnywhere.Model
{
	using IParamToken = IToken<IParam>;
	using ParamToken = Token<IParam>;

	using IActionToken = IToken<IAction>;
	using ActionToken = Token<IAction>;

	using CharStream = Stream<char>;
	using ParamTokenStream = Stream<IToken<IParam>>;
	using ActionTokenStream = Stream<IToken<IAction>>;
	
	public abstract class ActionMgr : IActionMgr
	{
		#region ctors
		public ActionMgr()
		{
			_CharStream = new Stream<char>();
			_ParamTokenStream = new Stream<IParamToken>();
			_ActionTokenStream = new Stream<IActionToken>();
			_ActionTokenStream.StreamChanged += OnStreamChanged;
			_CurrentDirectoryChanged += OnCurrentDirectoryChanged;
		}
		#endregion ctors

		#region props
		private bool _IsActive;
		public virtual bool IsActive
		{
			get { return _IsActive; }
			set
			{
				if (_IsActive != value)
				{
					_IsActive = value;
					
					if (ActionsChanged != null)
						CallActionsChanged();
				}
			}
		}
		private static string _UserInput;
		public string UserInput
		{
			get { return _UserInput; }
			set
			{
				if (_UserInput != value)
					Update(_UserInput = value);
			}
		}
		private static string _CurrentDirectory;
		public string CurrentDirectory
		{
			get { return _CurrentDirectory; }
			set
			{
				if (_CurrentDirectory != value)
				{
					_CurrentDirectory = value;
					if (_CurrentDirectoryChanged != null)
						_CurrentDirectoryChanged(_CurrentDirectory);
				}
			}
		}
		public int Dot
		{
			get
			{
				int dot = 0;
				IActionToken token = _ActionTokenStream.PeekItem();
				if (token != null)
					dot = token.Dot;
				return dot;
			}
		}
		public virtual List<IAction> Actions
		{
			get
			{
				List<IAction> actions = new List<IAction>();
				if (IsActive)
				{
					IActionToken token = _ActionTokenStream.PeekItem();
					if (token != null)
						actions = token.Children;
				}
				return actions;
			}
		}
		public virtual bool HasActions { get { return Count > 0; } }
		public IAction Current { get { return Actions[_Index]; } }
		public int Count { get { return Actions.Count; } }
		public abstract string[] Targets { get; }
		#endregion props

		#region methods
		public bool UserInputBackspace()
		{
			bool result = false;
			int length = UserInput.Length;
			if (length > 0)
			{
				UserInput = UserInput.Substring(0, length - 1);
				result = true;
			}
			return result;
		}
		public virtual int IncrIndex()
		{
			if (Count > 0)
			{
				_Index = (++_Index) % Count;
				if (ActionsChanged != null)
					CallActionsChanged();
			}
			return _Index;
		}
		public virtual int DecrIndex()
		{
			if (Count > 0)
			{
				_Index = (--_Index + Count) % Count;
				if (ActionsChanged != null)
					CallActionsChanged();
			}
			return _Index;
		}
		public virtual int ResetIndex()
		{
			if (_Index > 0)
			{
				_Index = 0;
				if (ActionsChanged != null)
					CallActionsChanged();
			}
			return _Index;
		}

		public abstract void Init();
		public abstract void End();
		public virtual void Reset()
		{
			_Index = 0;
			_Lexer.Clear();
			_Parser.Clear();
		}
		public virtual void Update(string input = null)
		{
			if (IsActive)
			{
				Reset();
				_Lexer.InputStream.AddItems(!string.IsNullOrEmpty(input) ? input.ToCharArray() : null);
				_Lexer.Evaluate();
				_Parser.Evaluate();
			}
		}

		public string Complete() { return Current.Name; }
		public abstract bool Advance();
		public abstract bool Withdraw();

		private void CallActionsChanged()
		{
			int dot = 0;
			IAction[] actions = null;
			IActionToken token = _ActionTokenStream.PeekItem();
			if (token != null)
			{
				dot = token.Dot;
				actions = token.Children.ToArray();
			}
			ActionsChanged(_Index, actions, dot);
		}
		private void OnStreamChanged(object sender, StreamChangedEventArgs e)
        {
			if (ActionsChanged != null)
			{
				int dot = 0;
				IAction[] actions = null;
				IActionToken token = _ActionTokenStream.PeekItem();
				if (token != null)
				{
					dot = token.Dot;
					actions = token.Children.ToArray();
				}
				ActionsChanged(_Index, actions, dot);
			}
        }
		public virtual void OnCurrentDirectoryChanged(string directory)
		{
			//Load(Targets);
		
		}
		public abstract void Load(params string[] targetsAndDirectories);
		protected List<ITarget> LoadTargets(string[] files)
		{
			List<ITarget> targets = new List<ITarget>();

			List<TargetsAndDirectories> tnds = DeserializeTargetsAndDirectories(files);
			foreach (var tnd in tnds)
			{
				foreach (TargetDirectories td in tnd.Directories)
				{
					targets.AddRange(td.Load());
				}
			}

			return targets;
		}

		protected List<TargetsAndDirectories> DeserializeTargetsAndDirectories(string[] files)
		{
			List<TargetsAndDirectories> tnds = new List<TargetsAndDirectories>();
			foreach (string file in files)
			{
				if (File.Exists(file) && Path.GetExtension(file) == ".xml")
				{
					using (StreamReader reader = new StreamReader(file))
					{
						try
						{
							XmlSerializer serializer = new XmlSerializer(typeof(TargetsAndDirectories));
							TargetsAndDirectories tnd = serializer.Deserialize(reader) as TargetsAndDirectories;
							if (tnd != null)
								tnds.Add(tnd);
						}
						catch (Exception ex)
						{
							Console.WriteLine(ex.Message);
						}
					}
				}
			}
			return tnds;
		}
		//public abstract void Load(params string[] targets);
		//protected TargetList LoadTargets(string[] targets)
		//{
		//    TargetList targetList = new TargetList();
		//    foreach (string target in targets)
		//    {
		//        FileAttributes attributes = File.GetAttributes(target);
		//        if ((attributes & FileAttributes.Directory) > 0 && Directory.Exists(target))
		//            targetList.AddRange(LoadTargetDirectory(target));
		//        else if (File.Exists(target))
		//            targetList.AddRange(LoadTargetFile(target));
		//        else
		//            throw new Exception("wtf");
		//    }
		//    return targetList;
		//}
		//protected TargetList LoadTargetDirectory(string directory)
		//{
		//    TargetList targetList = new TargetList();
		//    if (Directory.Exists(directory))
		//    {
		//        string[] files = Directory.GetFiles(directory);
		//        foreach (string file in files)
		//            targetList.AddRange(LoadTargetFile(file));
		//    }
		//    return targetList;
		//}
		//protected TargetList LoadTargetFile(string file)
		//{
		//    TargetList targetList = new TargetList();
		//    if (File.Exists(file))
		//    {
		//        switch (Path.GetExtension(file))
		//        {
		//            case ".xml":

		//                //using (StreamReader reader = new StreamReader(file))
		//                //{
		//                //    try
		//                //    {
		//                //        XmlSerializer serializer = new XmlSerializer(typeof(TargetList));
		//                //        TargetList tl = serializer.Deserialize(reader) as TargetList;
		//                //        targetList.AddRange(tl);
		//                //    }
		//                //    catch (Exception ex)
		//                //    {
		//                //        Console.WriteLine(ex.Message);
		//                //        targetList.Add(new Target(file));
		//                //    }
		//                //}
		//                break;
		//            default:
		//                targetList.Add(new Target(file));
		//                break;
		//        }
		//    }
		//    return targetList;
		//}



	
		#endregion methods

		#region events
		protected static event Action<string> _CurrentDirectoryChanged;
		public event Action<string> CurrentDirectoryChanged
		{
			add { _CurrentDirectoryChanged += value; }
			remove { _CurrentDirectoryChanged -= value; }
		}
		public event Action<int, IAction[], int> ActionsChanged;
		#endregion events

		#region fields
		protected static List<char> _ValidChars = new List<char>();

		protected CharStream _CharStream;
		protected ParamTokenStream _ParamTokenStream;
		protected ActionTokenStream _ActionTokenStream;

		protected Lexer _Lexer;
		protected Parser _Parser;
		protected int _Index;
		#endregion fields	
	}
}
